#include "bga/graphics/FrameBufferObject.h"

#include <iostream>

using namespace std;

namespace bga
{

FrameBufferObject::FrameBufferObject() : GLObject()
{
    /// generate object id
    this->id = generate();
}

FrameBufferObject::~FrameBufferObject()
{
    glDeleteFramebuffersEXT(1, &(this->id));
}

void FrameBufferObject::bind() const
{
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, this->id);
}

void FrameBufferObject::unbind() const
{
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}

void FrameBufferObject::attachTexture2D(GLenum attachment, Texture2D* texture2d)
{
    this->texture2dAttachment.insert( pair<GLenum, Texture2D*>(attachment, texture2d) );
}

void FrameBufferObject::applyAttachment()
{
    /// All attached texture must have same size
    unsigned int width = 0;
    unsigned int height = 0;

    if (this->texture2dAttachment.size() > 0)
    {
        std::map<GLenum, Texture2D*>::iterator it;
        for (it = this->texture2dAttachment.begin(); it != this->texture2dAttachment.end(); ++it)
        {
            width = (it->second)->getTextureWidth();
            height = (it->second)->getTextureHeight();

            /// configure texture to attach to
            (it->second)->bind();
            (it->second)->applyParameters();
            if ( !(it->second)->isAllocated() )
            {
                (it->second)->allocateTexture();
            }
            (it->second)->unbind();

            glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, it->first, GL_TEXTURE_2D, (it->second)->getId(), 0);
        }

        /// configure a rbo to attach depth and stencil component to have depth and stencil test
        rbo.setInternalFormat(GL_DEPTH_STENCIL);
        rbo.setWidth(width);
        rbo.setHeight(height);
        rbo.bind();
        rbo.allocateBuffer();
        rbo.unbind();

        /// attach RenderBuffer
        glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER_EXT, rbo.getId());
    }

    if (!isValid()) /// check if FBO is ready
    {
        cout<<"Error in FrameBufferObject::applyAttachment"<<endl;
    }
}

GLuint FrameBufferObject::generate() const
{
    GLuint id;
    glGenFramebuffersEXT(1, &id);
    return id;
}

bool FrameBufferObject::isValid()
{
    GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
    if(status != GL_FRAMEBUFFER_COMPLETE_EXT)
    {
        return false;
    }
    return true;
}

}

